Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

''' <summary>
''' Module to allow persistent storage of Stations and PCode lists
''' </summary>
Public Module modGraph
    Friend Const REGAPP As String = "WRDBGRAPH"
    Friend Const CALIBRATIONSTRING As String = "Calibration Stats..."

    Friend OpenTables As Generic.List(Of clsTable)
    Friend Layouts As New Generic.Dictionary(Of String, clsLayout)
    '    Friend Calibrations As New Generic.Dictionary(Of String, clsCalibration)
    Friend TableForm As frmTable
    Friend StatsForm As frmStats
    Friend GraphForm As frmGraph
    Friend GroupFileName As String = ""
    Friend ApplyToAllTabs As Boolean = False
    Friend ExpertMode As Boolean = False
    Friend UpdateLocation As String = "" 'todo: must set this in options form if want to allow automatic updates

    Friend AlreadyPromptedAddScatter As Boolean = False

    Friend Enum enumAxisType
        XAxis
        YAxis
    End Enum

    ''' <summary>
    ''' Global variable used only by frmOptions so TypeConverter knows how to offer up axis types
    ''' </summary>
    Friend _GraphType As enumGraphType

    ''' <summary>
    ''' List of allowable graph types to be displayed by frmGraph
    ''' </summary>
    Public Enum enumGraphType
        TimeSeries
        Profile
        Depth
        Width
        Scatter
        Histogram
        Probability
        FlowDuration
    End Enum

    Public Enum enumTimeWindow
        None
        _1_Minute
        _5_Minutes
        _15_Minutes
        _30_Minutes
        _1_Hour
        _2_Hours
        _6_Hours
        _12_Hours
        _1_Day
        _3_Days
        _1_Week
        _2_Weeks
        _1_Month
        _3_Months
        _6_Months
        _1_Year
        All
    End Enum

    ''' <summary>
    ''' Position of graph notes and series statistics
    ''' </summary>
    Public Enum enumNotePosition
        TopLeft
        TopCenter
        TopRight
        BottomLeft
        BottomCenter
        BottomRight
    End Enum

    Public Enum enumNoteAlignment
        Left
        Center
        Right
    End Enum

    Public Enum enumZoomOptions
        None
        Horizontal
        Vertical
        Both
        Auto
    End Enum

    Friend Structure structSineCurve
        Dim Period As Double
        Dim Mean As Double
        Dim Amplitude As Double
        Dim PhaseAngle As Double
        Dim StdDev As Double
        Dim Description As String
    End Structure

    Friend Structure structFitStats
        Dim XMin, XMax, YMin, YMax As Double
        Dim NumPoints As Integer
        Dim Slope As Double
        Dim Intercept As Double
        Dim CorrCoef As Double
    End Structure

    Friend Enum enumCurveFit
        Linear
        Power
        Exponential
    End Enum

    Public Enum enumProbFit
        Normal
        LogNormal
        LogPearson3
    End Enum

    Public Enum enumProbOption
        LessThan_Incr
        GreaterThan_Incr
        LessThan_Decr
        GreaterThan_Decr
    End Enum

    Public Structure structProbStats
        Dim NumPoints As Integer
        Dim Mean As Double
        Dim StdDev As Double
        Dim Skew As Double
        Dim MeanLog As Double
        Dim StdDevLog As Double
        Dim SkewLog As Double
    End Structure



    ' ''' <summary>
    ' ''' Helper routine to create standard font object given ZedFont object
    ' ''' </summary>
    ' ''' <param name="ZedFont">ZedGraph font object</param>
    ' ''' <returns>Standard .Net Font object</returns>
    'Friend Function GetFont(ByVal ZedFont As ZedGraph.FontSpec) As Font
    '    With ZedFont
    '        Dim fs As FontStyle = FontStyle.Regular
    '        Dim f As New Font(.Family, .Size / FontScaleFactor, IIf(.IsBold, FontStyle.Bold, 0) + IIf(.IsItalic, FontStyle.Italic, 0) + IIf(.IsUnderline, FontStyle.Underline, 0), GraphicsUnit.Point)
    '        Return f
    '    End With
    'End Function

    ''' <summary>
    ''' Helper routine to get ZedFont object (based on prototype) given standard font
    ''' </summary>
    ''' <param name="fnt">Standard .Net font object</param>
    ''' <param name="z">ZedGraph font prototype</param>
    ''' <returns>ZedGraph font object</returns>
    Friend Function GetFont(ByVal fnt As Font, ByVal z As ZedGraph.FontSpec, FontScaleFactor As Double) As ZedGraph.FontSpec
        With fnt
            z.Family = .FontFamily.Name
            z.Size = .Size * FontScaleFactor
            z.IsBold = .Bold
            z.IsItalic = .Italic
            z.IsUnderline = .Underline
            Return z
        End With
    End Function

    ' ''' <summary>
    ' ''' Reset all graph fonts for all tabs (by reassigning font, gets pushed through to zed)
    ' ''' </summary>
    'Friend Sub ResetFontScaleFactor()

    '    For t As Integer = 0 To GraphForm.TabGraph.TabCount - 2
    '        With ActiveGraphTab(t).GraphOptions
    '            .TitleFont = .TitleFont
    '            .LegendFont = .LegendFont
    '            .NoteFont = .NoteFont
    '            For i As Integer = 1 To 2
    '                Dim axis As New clsAxis(._Zed, Choose(i, clsAxis.enumAxis.XAxis, clsAxis.enumAxis.X2Axis), 0)
    '                axis.TitleFont = axis.TitleFont
    '                axis.LabelFont = axis.LabelFont
    '                For j As Integer = 0 To 4
    '                    axis = New clsAxis(._Zed, Choose(i, clsAxis.enumAxis.YAxis, clsAxis.enumAxis.Y2Axis), j)
    '                    axis.TitleFont = axis.TitleFont
    '                    axis.LabelFont = axis.LabelFont
    '                Next
    '            Next
    '        End With
    '        ActiveGraphTab(t).Zed.Refresh()
    '    Next
    'End Sub

    ''' <summary>
    ''' Name of directory where data resides (and default for new projects)
    ''' </summary>
    Friend AppDataPath As String = String.Format("{0}\WRDB", My.Computer.FileSystem.SpecialDirectories.MyDocuments)

    ''' <summary>
    ''' The currently active GraphTab object, stored in the tabpage's tag object
    ''' </summary>
    Friend Function ActiveGraphTab() As clsGraphTab
        If GraphForm Is Nothing OrElse GraphForm.TabGraph.SelectedTab Is Nothing Then Return Nothing Else Return GraphForm.TabGraph.SelectedTab.Tag
    End Function

    ''' <summary>
    ''' The GraphTab object for a specific tab pag, stored in that tabpage's tag object
    ''' </summary>
    Friend Function ActiveGraphTab(ByVal Index As Integer) As clsGraphTab
        Return GraphForm.TabGraph.TabPages(Index).Tag
    End Function

    ''' <summary>
    ''' This routine should handle problems with fact that axis limit settings changed from numeric to string, and legacy files must be handled properly
    ''' </summary>
    ''' <param name="Limit">The value of the limit read from the .graph file</param>
    ''' <param name="AxisType">Axis type (to detect if is date)</param>
    ''' <param name="AxisAuto">Will set this auto min/max property</param>
    ''' <param name="AxisMinMax">Will set this min/max property</param>
    ''' <remarks></remarks>
    Public Sub SetAxisLimit(ByVal Limit As String, ByVal AxisType As AxisType, ByRef AxisAuto As Boolean, ByRef AxisMinMax As Double)
        If Limit = "Not Set" OrElse (IsNumeric(Limit) AndAlso Math.Abs(Val(Limit)) > 1.0E+300) Then
            AxisAuto = True
        ElseIf IsNumeric(Limit) Then
            AxisAuto = False
            AxisMinMax = Limit
        ElseIf IsDate(Limit) And (AxisType = AxisType.Date Or AxisType = AxisType.DateDual) Then
            AxisAuto = False
            AxisMinMax = CDate(Limit).ToOADate
        Else
            AxisAuto = True
        End If
    End Sub

    ''' <summary>
    ''' Given calibration name, return associated table object that holds it
    ''' </summary>
    ''' <param name="CalibrationName"></param>
    Public Function CalibrationTable(CalibrationName As String) As clsTable
        For Each tbl As clsTable In OpenTables
            If tbl.TableType = clsTable.enumTableType.Calibration AndAlso tbl.TableName = CalibrationName Then Return tbl
        Next
        Return Nothing
    End Function

    ''----------------------------------------------
    ''Here are two methods for dealing with document properties
    ''first uses shell 32 to retrieve (but cannot save back to file!)
    ''second uses DSO Document method (read-write)

    ''GetDetailedFileInfo(BMDFile)
    ''fnGetPropDlg(BMDFile)

    ''following requires reference to DSO Document... library downloaded form Microsoft (and stored in C:\DSOFile)
    'Friend Sub SetComments(ByVal FileName As String)
    '    Dim dso As New DSOFile.OleDocumentProperties
    '    dso.Open(FileName)
    '    dso.SummaryProperties.Comments = InputBox("Enter new comments:", , dso.SummaryProperties.Comments)
    '    dso.Save()
    '    dso.Close()
    'End Sub



    '''' <summary>
    '''' Get detailed file properties information
    '''' </summary>
    '''' <param name="FileName">Name of file desired</param>
    '''' <returns>String with property</returns>
    '''' <remarks>Must add COM reference "Microsoft Shell Controls and Automation" (shell32.dll) to your Project</remarks>
    'Friend Function GetDetailedFileInfo(ByVal FileName As String) As Dictionary(Of String, String)
    '    If My.Computer.FileSystem.FileExists(FileName) Then
    '        Dim sh As New Shell32.Shell
    '        Dim dir As Shell32.Folder = sh.NameSpace(IO.Path.GetDirectoryName(FileName))
    '        Dim dict As New Dictionary(Of String, String)
    '        For i As Integer = 0 To 33
    '            Dim key As String = dir.GetDetailsOf(dir.Items, i)
    '            Dim value As String = dir.GetDetailsOf(dir.ParseName(IO.Path.GetFileName(FileName)), i)
    '            If Not dict.ContainsKey(key) Then dict.Add(key, value)
    '        Next
    '        Return dict
    '    Else
    '        Throw new applicationexception("File not found")
    '        Return Nothing
    '    End If
    'End Function

    'Public Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal Hwnd As Integer, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Integer) As Integer
    'Private Declare Function ShellExecuteEx Lib "shell32.dll" (ByRef Prop As SHELLEXECUTEINFO) As Integer

    ''// Properties API
    'Private Structure SHELLEXECUTEINFO
    '    Dim cbSize As Integer
    '    Dim fMask As Integer
    '    Dim Hwnd As Integer
    '    Dim lpVerb As String
    '    Dim lpFile As String
    '    Dim lpParameters As String
    '    Dim lpDirectory As String
    '    Dim nShow As Integer
    '    Dim hInstApp As Integer
    '    Dim lpIDList As Integer
    '    Dim lpClass As String
    '    Dim hkeyClass As Integer
    '    Dim dwHotKey As Integer
    '    Dim hIcon As Integer
    '    Dim hProcess As Integer
    'End Structure


    'Public Function fnGetPropDlg(ByRef strFilepath As String) As Integer
    '    Dim Prop As SHELLEXECUTEINFO

    '    With Prop
    '        .cbSize = Len(Prop)
    '        .fMask = &HC
    '        .Hwnd = 0
    '        .lpVerb = "properties"
    '        .lpFile = strFilepath
    '    End With

    '    fnGetPropDlg = ShellExecuteEx(Prop)

    'End Function
    ''' <summary>
    ''' Verify whether file exists in one of two locations; if not, offer to browse to reselect
    ''' </summary>
    ''' <param name="FullFilePath">Full absolute file path to first location</param>
    ''' <param name="BaseDirectory">Full absolute directory path to second location</param>
    ''' <param name="RelativeFilePath">Relative file path to second location</param>
    ''' <returns>Full path of file if found or selected; empty string if cancel at file dialog</returns>
    ''' <remarks>Will first look for file containing full path, then for file at relative location, then in base directory, then will browse</remarks>
    Friend Function FindFile(ByVal FullFilePath As String, Optional ByVal BaseDirectory As String = "", Optional ByVal RelativeFilePath As String = "") As String

        'Static UseSelection As Boolean = False
        Static UseSelection As Boolean = True 'for now, never show dialog and always select in order 

        Dim OrigPath As String = "", RelPath As String = "", LocalPath As String = ""
        Dim NumFound As Integer = 0

        If FullFilePath <> "" And My.Computer.FileSystem.FileExists(FullFilePath) Then OrigPath = FullFilePath : NumFound += 1
        Dim FullFilePath2 As String = ""
        If BaseDirectory <> "" AndAlso My.Computer.FileSystem.DirectoryExists(BaseDirectory) Then
            My.Computer.FileSystem.CurrentDirectory = BaseDirectory
            If RelativeFilePath <> "" Then
                FullFilePath2 = IO.Path.GetFullPath(RelativeFilePath)
                If My.Computer.FileSystem.FileExists(FullFilePath2) Then RelPath = FullFilePath2 : NumFound += 1
            End If
            FullFilePath2 = IO.Path.GetFullPath(BaseDirectory & "\" & IO.Path.GetFileName(FullFilePath))
            If My.Computer.FileSystem.FileExists(FullFilePath2) Then LocalPath = FullFilePath2 : NumFound += 1
            If RelPath = LocalPath Then RelPath = "" : NumFound -= 1
            If OrigPath = LocalPath Then OrigPath = "" : NumFound -= 1
        End If

        Dim path() As String = {OrigPath, RelPath, LocalPath}

        If NumFound = 1 Or UseSelection Then
            If OrigPath <> "" Then Return OrigPath
            If RelPath <> "" Then Return RelPath
            Return LocalPath
        Else
            If NumFound > 1 Then
                With New frmFindFile
                    Dim opt() As RadioButton = {.optOrig, .optRel, .optLocal}
                    Dim txt() As TextBox = {.txtOrig, .txtRel, .txtLocal}
                    For i As Integer = 0 To 2
                        If path(i) <> "" Then
                            opt(i).Enabled = True
                            txt(i).Text = path(i)
                            .ToolTip1.SetToolTip(txt(i), path(i))
                            opt(i).Checked = True 'set last one
                        Else
                            opt(i).Enabled = False
                        End If
                    Next
                    If .ShowDialog = DialogResult.OK Then
                        For i As Integer = 0 To 2
                            If opt(i).Checked Then
                                UseSelection = .chkDontDisplay.Checked
                                Return path(i)
                            End If
                        Next
                        'will browse below
                    Else
                        Return ""
                    End If
                    .Dispose()
                End With
            End If
        End If

        'no files found on any path or user selected browse option

        With New OpenFileDialog
            .FileName = IO.Path.GetFileName(FullFilePath)
            .InitialDirectory = IO.Path.GetDirectoryName(FullFilePath)
            Dim ext As String = IO.Path.GetExtension(FullFilePath)
            .DefaultExt = ext
            .Filter = String.Format("{0} files|*{0}|All files|*.*", ext)
            .FilterIndex = 0
            .Multiselect = False
            .Title = "File not found; please browse to select"
            If .ShowDialog = DialogResult.OK Then
                If .FileName <> "" And My.Computer.FileSystem.FileExists(.FileName) Then Return .FileName
            End If
            Return ""
        End With

        'If FullFilePath <> "" And My.Computer.FileSystem.FileExists(FullFilePath) Then Return FullFilePath
        'Dim FullFilePath2 As String = ""
        'If BaseDirectory <> "" Then
        '    My.Computer.FileSystem.CurrentDirectory = BaseDirectory
        '    If RelativeFilePath <> "" Then
        '        FullFilePath2 = IO.Path.GetFullPath(RelativeFilePath)
        '        If My.Computer.FileSystem.FileExists(FullFilePath2) Then Return FullFilePath2
        '    End If
        '    FullFilePath2 = IO.Path.GetFullPath(BaseDirectory & "\" & IO.Path.GetFileName(FullFilePath))
        '    If My.Computer.FileSystem.FileExists(FullFilePath2) Then Return FullFilePath2
        'End If
        'With New OpenFileDialog
        '    .FileName = IO.Path.GetFileName(FullFilePath)
        '    .InitialDirectory = IO.Path.GetDirectoryName(FullFilePath)
        '    Dim ext As String = IO.Path.GetExtension(FullFilePath)
        '    .DefaultExt = ext
        '    .Filter = String.Format("{0} files|*{0}|All files|*.*", ext)
        '    .FilterIndex = 0
        '    .Multiselect = False
        '    .Title = "File not found; please browse to select"
        '    If .ShowDialog = DialogResult.OK Then
        '        If .FileName <> "" And My.Computer.FileSystem.FileExists(.FileName) Then Return .FileName
        '    End If
        '    Return ""
        'End With
    End Function

    Friend Sub ShowHelp()
        Dim HelpFile As String = My.Application.Info.DirectoryPath & "\WRDBGraph.chm"
        If Debugger.IsAttached Then HelpFile = "C:\Projects\WRDB 5.0\Help\HTMLHelp\WRDBGraph.chm"
        If My.Computer.FileSystem.FileExists(HelpFile) Then
            Help.ShowHelp(Form.ActiveForm, HelpFile)
        Else
            WarningMsg("Unable to find help file: {0}", HelpFile.Replace("\", "/"))
        End If
    End Sub

    Friend Sub ShowHelp(ByVal OwnerForm As Form, ByVal TopicID As String)
        'note: to assign map IDs to help topics, in RoboHelp HTML select Project tab, then Context Sensitive Help, then Aliases, then Map File WRDB.h
        Dim HelpFile As String = My.Application.Info.DirectoryPath & "\WRDBGraph.chm"
        If Debugger.IsAttached Then HelpFile = "C:\Projects\WRDB 5.0\HELP\HTMLHelp\WRDBGraph.chm"
        If My.Computer.FileSystem.FileExists(HelpFile) Then
            Help.ShowHelp(OwnerForm, HelpFile, HelpNavigator.TopicId, TopicID)
        Else
            WarningMsg(StringFormat("Unable to find help file named '{0}'; please restore this file or reinstall application.", HelpFile.Replace("\", "\\")))
        End If
    End Sub

    Friend WithEvents Updates As New clsUpdates("WRDB", "www.wrdb.com")
    Friend ShutdownCommandline As String = ""

    Friend Function UpdatesInstalled(ByVal ShowIsUpdatedMsg As Boolean) As Boolean
        If Updates.UpdatesInstalled(UpdateLocation, ShowIsUpdatedMsg, ShutdownCommandline) Then
            GraphForm.Close()
            Return True
        Else
            Return False
        End If
    End Function

End Module

''' <summary>
''' See the Microsoft article http://support.microsoft.com/?id=323530 for details. 
''' </summary>
Public Class ClipboardMetafileHelper

    <DllImport("user32.dll", EntryPoint:="OpenClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
    End Function

    <DllImport("user32.dll", EntryPoint:="EmptyClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function EmptyClipboard() As Boolean
    End Function

    <DllImport("user32.dll", EntryPoint:="SetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal hWnd As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", EntryPoint:="CloseClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function CloseClipboard() As Boolean
    End Function

    <DllImport("gdi32.dll", EntryPoint:="CopyEnhMetaFileA", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function CopyEnhMetaFile(ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr
    End Function

    <DllImport("gdi32.dll", EntryPoint:="DeleteEnhMetaFile", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function DeleteEnhMetaFile(ByVal hemfSrc As IntPtr) As Boolean
    End Function

    ' Metafile mf is set to a state that is not valid inside this function.
    Public Shared Function PutImageOnClipboard(ByVal hWnd As IntPtr, ByVal mf As Metafile) As Boolean
        Try
            Dim bResult As New Boolean()
            bResult = False
            Dim hEMF, hEMF2 As IntPtr
            hEMF = mf.GetHenhmetafile() ' invalidates mf
            If Not hEMF.Equals(New IntPtr(0)) Then
                hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))
                If Not hEMF2.Equals(New IntPtr(0)) Then
                    If OpenClipboard(hWnd) Then
                        If EmptyClipboard() Then
                            Dim hRes As IntPtr
                            hRes = SetClipboardData(14, hEMF2)    ' 14 == CF_ENHMETAFILE
                            bResult = hRes.Equals(hEMF2)
                            CloseClipboard()
                        End If
                    End If
                End If
                DeleteEnhMetaFile(hEMF)
            End If
            Return bResult
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        End Try
    End Function

    Public Shared Sub CopyToClip(ByVal ZedGraphPane As ZedGraph.GraphPane)
        Dim g As Graphics = GraphForm.CreateGraphics
        Dim hdc As IntPtr = g.GetHdc
        Dim metaFile = New Metafile(hdc, EmfType.EmfOnly)
        g.ReleaseHdc(hdc)
        g.Dispose()

        Dim gMeta As Graphics = Graphics.FromImage(metaFile)
        ZedGraphPane.Draw(gMeta)
        gMeta.Dispose()

        PutImageOnClipboard(GraphForm.Handle, metaFile)
    End Sub

    Public Shared Sub CopyToClip(ByVal PageImage As Bitmap)
        Dim g As Graphics = GraphForm.CreateGraphics
        Dim hdc As IntPtr = g.GetHdc
        Dim metaFile = New Metafile(hdc, EmfType.EmfOnly)
        g.ReleaseHdc(hdc)
        g.Dispose()

        Dim gMeta As Graphics = Graphics.FromImage(metaFile)
        gMeta = Graphics.FromImage(PageImage)
        gMeta.Dispose()

        PutImageOnClipboard(GraphForm.Handle, metaFile)
    End Sub

End Class

